//
//  LBLevelFilter.mm
//  OpenGLGame
//
//  Created by Nguyen Cong Huy on 4/3/13.
//  Copyright (c) 2013 UIT. All rights reserved.
//

#include "LBLevelFilter.h"
#include "LBGraphics.h"

char fShaderStrLevel[] = ""
"precision mediump float;"

"varying vec2 vtexCoor;"
"varying vec4 vposition;"
"uniform sampler2D texture;"

"uniform float minInput;"
"uniform float maxInput;"
"uniform float gamma;"
"uniform float minOutput;"
"uniform float maxOutput;"

"uniform float minInputR;"
"uniform float maxInputR;"
"uniform float gammaR;"
"uniform float minOutputR;"
"uniform float maxOutputR;"

"uniform float minInputG;"
"uniform float maxInputG;"
"uniform float gammaG;"
"uniform float minOutputG;"
"uniform float maxOutputG;"

"uniform float minInputB;"
"uniform float maxInputB;"
"uniform float gammaB;"
"uniform float minOutputB;"
"uniform float maxOutputB;"


// level function
"vec4 gammaCorrection(vec4 color, float gamma){"
"    vec4 result = pow(color, vec4(1.0 / gamma));"
"    result.a = color.a;"
"    return result;"
"}"

"vec4 levelsControlInputRange(vec4 color, float minInput, float maxInput){"
"    vec4 result = min(max(color - vec4(minInput), vec4(0.0)) / (vec4(maxInput) - vec4(minInput)), vec4(1.0));"
"   result.a = color.a;"
"   return result;"
"}"

"vec4 levelsControlInput(vec4 color, float minInput, float gamma, float maxInput){"
"    return gammaCorrection(levelsControlInputRange(color, minInput, maxInput), gamma);"
"}"

"vec4 levelsControlOutputRange(vec4  color, float minOutput, float maxOutput){"
"    vec4 result =  mix(vec4(minOutput), vec4(maxOutput), color);"
"    result.a = color.a;"
"    return result;"
"}"

"vec4 levelsControl(vec4 color, float minInput, float gamma, float maxInput, float minOutput, float maxOutput){"
" 	vec4 result = levelsControlOutputRange(levelsControlInput(color, minInput, gamma, maxInput), minOutput, maxOutput);"
"   return result;"
"}"

// END level function

// level function float
"float gammaCorrectionF(float color, float gamma){"
"    float result = pow(color, 1.0 / gamma);"
"    return result;"
"}"

"float levelsControlInputRangeF(float color, float minInput, float maxInput){"
"    float result = min(max(color - minInput, 0.0) / (maxInput - minInput), 1.0);"
"   return result;"
"}"

"float levelsControlInputF(float color, float minInput, float gamma, float maxInput){"
"    return gammaCorrectionF(levelsControlInputRangeF(color, minInput, maxInput), gamma);"
"}"

"float levelsControlOutputRangeF(float  color, float minOutput, float maxOutput){"
"    float result =  mix(minOutput, maxOutput, color);"
"    return result;"
"}"

"float levelsControlF(float color, float minInput, float gamma, float maxInput, float minOutput, float maxOutput){"
" 	float result = levelsControlOutputRangeF(levelsControlInputF(color, minInput, gamma, maxInput), minOutput, maxOutput);"
"   return result;"
"}"

// END level function float


"void main()"
"{"
"    lowp vec4 textureColor = texture2D(texture, vtexCoor);"

"    if(minInput != 0.0 || maxInput != 1.0 || gamma != 1.0 || minOutput != 0.0 || maxOutput != 1.0){"
"       textureColor = levelsControl(textureColor, minInput, gamma, maxInput, minOutput, maxOutput);"
"    }"

"    if(minInputR != 0.0 || maxInputR != 1.0 || gammaR != 1.0 || minOutputR != 0.0 || maxOutputR != 1.0){"
"       textureColor.r = levelsControlF(textureColor.r, minInputR, gammaR, maxInputR, minOutputR, maxOutputR);"
"    }"

"    if(minInputG != 0.0 || maxInputG != 1.0 || gammaG != 1.0 || minOutputG != 0.0 || maxOutputG != 1.0){"
"       textureColor.g = levelsControlF(textureColor.g, minInputG, gammaG, maxInputG, minOutputG, maxOutputG);"
"    }"

"    if(minInputB != 0.0 || maxInputB != 1.0 || gammaB != 1.0 || minOutputB != 0.0 || maxOutputB != 1.0){"
"       textureColor.b = levelsControlF(textureColor.b, minInputB, gammaB, maxInputB, minOutputB, maxOutputB);"
"    }"

"    gl_FragColor = textureColor;"
"}";

LBLevelFilter::LBLevelFilter(){
    init(0, fShaderStrLevel);
    LBGraphics::getInstance()->useProgram(program);
    minInputUniform = program->uniformIndex("minInput");
    maxInputUniform = program->uniformIndex("maxInput");
    gammaUniform = program->uniformIndex("gamma");
    minOutputUniform = program->uniformIndex("minOutput");
    maxOutputUniform = program->uniformIndex("maxOutput");
    
    minInputRUniform = program->uniformIndex("minInputR");
    maxInputRUniform = program->uniformIndex("maxInputR");
    gammaRUniform = program->uniformIndex("gammaR");
    minOutputRUniform = program->uniformIndex("minOutputR");
    maxOutputRUniform = program->uniformIndex("maxOutputR");
    
    minInputGUniform = program->uniformIndex("minInputG");
    maxInputGUniform = program->uniformIndex("maxInputG");
    gammaGUniform = program->uniformIndex("gammaG");
    minOutputGUniform = program->uniformIndex("minOutputG");
    maxOutputGUniform = program->uniformIndex("maxOutputG");
    
    minInputBUniform = program->uniformIndex("minInputB");
    maxInputBUniform = program->uniformIndex("maxInputB");
    gammaBUniform = program->uniformIndex("gammaB");
    minOutputBUniform = program->uniformIndex("minOutputB");
    maxOutputBUniform = program->uniformIndex("maxOutputB");
    
    setMinInput(0.0f);
    setMaxInput(1.0f);
    setGamma(1.0f);
    setMinOutput(0.0f);
    setMaxOutput(1.0f);
    
    setMinInputR(0.0f);
    setMaxInputR(1.0f);
    setGammaR(1.0f);
    setMinOutputR(0.0f);
    setMaxOutputR(1.0f);
    
    setMinInputG(0.0f);
    setMaxInputG(1.0f);
    setGammaG(1.0f);
    setMinOutputG(0.0f);
    setMaxOutputG(1.0f);
    
    setMinInputB(0.0f);
    setMaxInputB(1.0f);
    setGammaB(1.0f);
    setMinOutputB(0.0f);
    setMaxOutputB(1.0f);
    
}

void LBLevelFilter::setMinInput(float minInput){
    this->minInput = minInput;
    LBGraphics::getInstance()->useProgram(program);
    glUniform1f(minInputUniform, minInput);
}

void LBLevelFilter::setMaxInput(float maxInput){
    this->maxInput = maxInput;
    LBGraphics::getInstance()->useProgram(program);
    glUniform1f(maxInputUniform, maxInput);
}

void LBLevelFilter::setGamma(float gamma){
    this->gamma = gamma;
    LBGraphics::getInstance()->useProgram(program);
    glUniform1f(gammaUniform, gamma);
}

void LBLevelFilter::setMinOutput(float minOutput){
    this->minOutput = minOutput;
    LBGraphics::getInstance()->useProgram(program);
    glUniform1f(minOutputUniform, minOutput);
}

void LBLevelFilter::setMaxOutput(float maxOutput){
    this->maxOutput = maxOutput;
    LBGraphics::getInstance()->useProgram(program);
    glUniform1f(maxOutputUniform, maxOutput);
}

void LBLevelFilter::setMinInputR(float minInputR){
    this->minInputR = minInputR;
    LBGraphics::getInstance()->useProgram(program);
    glUniform1f(minInputRUniform, minInputR);
}

void LBLevelFilter::setMaxInputR(float maxInputR){
    this->maxInputR = maxInputR;
    LBGraphics::getInstance()->useProgram(program);
    glUniform1f(maxInputRUniform, maxInputR);
}

void LBLevelFilter::setGammaR(float gammaR){
    this->gammaR = gammaR;
    LBGraphics::getInstance()->useProgram(program);
    glUniform1f(gammaRUniform, gammaR);
}

void LBLevelFilter::setMinOutputR(float minOutputR){
    this->minOutputR = minOutputR;
    LBGraphics::getInstance()->useProgram(program);
    glUniform1f(minOutputRUniform, minOutputR);
}

void LBLevelFilter::setMaxOutputR(float maxOutputR){
    this->maxOutputR = maxOutputR;
    LBGraphics::getInstance()->useProgram(program);
    glUniform1f(maxOutputRUniform, maxOutputR);
}

void LBLevelFilter::setMinInputG(float minInputG){
    this->minInputG = minInputG;
    LBGraphics::getInstance()->useProgram(program);
    glUniform1f(minInputGUniform, minInputG);
}

void LBLevelFilter::setMaxInputG(float maxInputG){
    this->maxInputG = maxInputG;
    LBGraphics::getInstance()->useProgram(program);
    glUniform1f(maxInputGUniform, maxInputG);
}

void LBLevelFilter::setGammaG(float gammaG){
    this->gammaG = gammaG;
    LBGraphics::getInstance()->useProgram(program);
    glUniform1f(gammaGUniform, gammaG);
}

void LBLevelFilter::setMinOutputG(float minOutputG){
    this->minOutputG = minOutputG;
    LBGraphics::getInstance()->useProgram(program);
    glUniform1f(minOutputGUniform, minOutputG);
}

void LBLevelFilter::setMaxOutputG(float maxOutputG){
    this->maxOutputG = maxOutputG;
    LBGraphics::getInstance()->useProgram(program);
    glUniform1f(maxOutputGUniform, maxOutputG);
}

void LBLevelFilter::setMinInputB(float minInputB){
    this->minInputB = minInputB;
    LBGraphics::getInstance()->useProgram(program);
    glUniform1f(minInputBUniform, minInputB);
}

void LBLevelFilter::setMaxInputB(float maxInputB){
    this->maxInputB = maxInputB;
    LBGraphics::getInstance()->useProgram(program);
    glUniform1f(maxInputBUniform, maxInputB);
}

void LBLevelFilter::setGammaB(float gammaB){
    this->gammaB = gammaB;
    LBGraphics::getInstance()->useProgram(program);
    glUniform1f(gammaBUniform, gammaB);
}

void LBLevelFilter::setMinOutputB(float minOutputB){
    this->minOutputB = minOutputB;
    LBGraphics::getInstance()->useProgram(program);
    glUniform1f(minOutputBUniform, minOutputB);
}

void LBLevelFilter::setMaxOutputB(float maxOutputB){
    this->maxOutputB = maxOutputB;
    LBGraphics::getInstance()->useProgram(program);
    glUniform1f(maxOutputBUniform, maxOutputB);
}